PriceChartInner.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. 'use client';
  2. import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
  3. import type { StockPriceRow } from '@/types/stock';
  4. import { changeDirection, formatNumber } from '@/lib/utils/stock';
  5. // recharts(d3 포함) 실제 렌더 본체 — StockPriceChart 가 next/dynamic({ssr:false})로 지연 로드.
  6. // 색은 래퍼 dir 클래스(.stock-chart--up/down/flat)의 CSS color 를 currentColor 로 상속
  7. // (recharts stroke/fill 은 SVG presentation attribute 라 var() 미해석 → currentColor 사용).
  8. type Props = {
  9. // GetDetail.recentPrices — 최신순(newest-first). 호출부에서 length>=2 보장.
  10. prices: StockPriceRow[];
  11. name: string;
  12. };
  13. export default function PriceChartInner({ prices, name }: Props)
  14. {
  15. // 과거→최신 순서로 뒤집어 종가 시계열 구성
  16. const series = [...prices].reverse().map(p => ({ date: p.tradingDate, close: p.close }));
  17. const dir = changeDirection(series[series.length - 1].close - series[0].close);
  18. return (
  19. <figure className={`stock-chart stock-chart--${dir}`}>
  20. <figcaption className='stock-chart__caption'>최근 {series.length}일 종가 추이</figcaption>
  21. <div className='stock-chart__canvas' aria-label={`${name} 최근 ${series.length}일 종가 추이 그래프`} role='img'>
  22. <ResponsiveContainer width='100%' height='100%'>
  23. <AreaChart data={series} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>
  24. <XAxis dataKey='date' hide />
  25. <YAxis hide domain={['dataMin', 'dataMax']} />
  26. <Tooltip
  27. cursor={{ stroke: 'currentColor', strokeOpacity: 0.3 }}
  28. contentStyle={{
  29. background: 'var(--bg-elevated)',
  30. border: '1px solid var(--border-default)',
  31. borderRadius: 0,
  32. fontSize: 'var(--fs-xs)',
  33. color: 'var(--text-primary)'
  34. }}
  35. labelStyle={{ color: 'var(--text-muted)' }}
  36. formatter={(value) => {
  37. const n = Array.isArray(value) ? Number(value[0]) : Number(value);
  38. return [formatNumber(Number.isFinite(n) ? n : null), '종가'];
  39. }}
  40. labelFormatter={(label) => String(label)}
  41. />
  42. <Area
  43. type='monotone'
  44. dataKey='close'
  45. stroke='currentColor'
  46. strokeWidth={2}
  47. fill='currentColor'
  48. fillOpacity={0.1}
  49. dot={false}
  50. isAnimationActive={false}
  51. />
  52. </AreaChart>
  53. </ResponsiveContainer>
  54. </div>
  55. </figure>
  56. );
  57. }